/* eslint-disable no-unused-expressions */
/* eslint-disable no-param-reassign */
import { clone } from '@ibiz-template/core';
import { IDEChart, IDEChartSeries } from '@ibiz/model-core';
import type { EChartsType } from 'echarts';
import {
  ValueOP,
  CodeListItem,
  ChartController,
  MDCtrlLoadParams,
  ISearchCondGroup,
  OpenAppViewCommand,
  ChartOptionsGenerator,
} from '@ibiz-template/runtime';
import { IApiMDGroupParams, IOdooChartState } from './interface';
import { formatDate, getDateRange } from './util';

/**
 * @description odoo图表控制器
 * @export
 * @class OdooChartController
 * @extends {ChartController}
 */
export class OdooChartController extends ChartController {
  /**
   * @description odoo图表状态对象
   * @type {IOdooChartState}
   * @memberof OdooChartController
   */
  declare state: IOdooChartState;

  /**
   * @description 分组参数
   * @private
   * @type {IApiMDGroupParams[]}
   * @memberof OdooChartController
   */
  private groupParams: IApiMDGroupParams[] = [];

  /**
   * @description 数据集（原始值）
   * @private
   * @type {IData[]}
   * @memberof OdooChartController
   */
  private items: IData[] = [];

  /**
   * @description 序列属性代码表
   * @private
   * @type {Map<string, readonly CodeListItem[]>}
   * @memberof OdooChartController
   */
  private seriesFieldCodelist: Map<string, readonly CodeListItem[]> = new Map();

  /**
   * @description 原始模型
   * @private
   * @type {IDEChart}
   * @memberof OdooChartController
   */
  private originalModel!: IDEChart;

  /**
   * @description 初始化state
   * @protected
   * @memberof OdooChartController
   */
  protected initState(): void {
    super.initState();
    const { dechartSerieses } = this.model;
    this.state.surveys = [];
    this.state.stacked = false;
    this.state.chartSort = undefined;
    this.state.cumulative = false;
    this.state.dechartSerieses = dechartSerieses || [];
    this.state.selectSeries = clone(this.state.dechartSerieses[0]);
    this.state.selectSurvey = this.state.selectSeries.valueField;
  }

  /**
   * @description 生命周期-创建完成
   * @protected
   * @returns {*}  {Promise<void>}
   * @memberof OdooChartController
   */
  protected async onCreated(): Promise<void> {
    await super.onCreated();
    (this as IData).model = clone(this.model);
    this.originalModel = clone(this.model);
    await this.initGenerator();
    this.initSurveys();
  }

  /**
   * @description 根据数据计算最终的options 并刷新echarts
   * @returns {*}  {Promise<void>}
   * @memberof OdooChartController
   */
  async calcOptions(data: IData[] = this.state.items): Promise<void> {
    const { cumulative, chartSort, selectSeries } = this.state;
    this.options = await this.generator.calcOptionsByData(
      data,
      this.context,
      this.params,
    );
    if (cumulative && selectSeries.echartsType === 'line') {
      // 累加
      (this.options.series as IData[])?.forEach(line => {
        line.data =
          (line.data as unknown[][])?.map((item, index) => {
            if (index) item[1] += line.data[index - 1][1];
            return item;
          }) || [];
      });
    } else if (
      chartSort &&
      selectSeries.echartsType &&
      ['bar', 'line'].includes(selectSeries.echartsType)
    ) {
      // 排序
      const map = new Map();
      (this.options.series as IData[])?.forEach(line => {
        (line.data as unknown[][])?.forEach(item => {
          const value = map.get(item[0]) || 0;
          map.set(item[0], value + item[1]);
        });
      });
      const sortedKeys = Array.from(map.keys()).sort(
        (a, b) => map.get(a) - map.get(b),
      );
      if (chartSort === 'Descending') sortedKeys.reverse();
      (this.options.series as IData[])?.forEach(line => {
        line.data = (line.data as unknown[][]).sort((a, b) => {
          return sortedKeys.indexOf(a[0]) - sortedKeys.indexOf(b[0]);
        });
      });
    }
    if (this.chart) this.updateChart();
  }

  /**
   * @description 初始化生成器
   * @private
   * @memberof OdooChartController
   */
  private async initGenerator(): Promise<void> {
    const { selectSeries } = this.state;
    if (!selectSeries.userParam) selectSeries.userParam = {};
    // 仅折线图补全数据
    Object.assign(selectSeries.userParam, {
      completiondate: selectSeries.echartsType === 'line' ? 'true' : 'false',
    });
    this.model.dechartSerieses = [selectSeries];
    // 饼图不要坐标系
    this.model.chartXAxises =
      selectSeries.echartsType === 'pie'
        ? undefined
        : this.originalModel.chartXAxises;
    this.model.chartYAxises =
      selectSeries.echartsType === 'pie'
        ? undefined
        : this.originalModel.chartYAxises;
    this.generator = new ChartOptionsGenerator(this.model, {
      chartId: this.controlParams.chartid,
    });
    await this.generator.init(this.context, this.params);
    if (this.state.isLoaded) {
      await this.calcOptions();
      await this.updateChart();
    }
  }

  /**
   * @description 处理图表点击
   * @private
   * @param item
   * @memberof OdooChartController
   */
  private async handleChartClick(item: IData): Promise<void> {
    const { navAppViewId, catalogField, seriesField, groupMode } =
      this.state.selectSeries;
    if (!navAppViewId) return;
    const searchcond: ISearchCondGroup = {
      condtype: 'GROUP',
      condop: 'AND',
      searchconds: [],
    };

    // 计算反查参数
    const addParam = (field: string | undefined, format?: string): void => {
      if (!field) return;

      const fieldKey = field.toLowerCase();
      const fieldValue = item[fieldKey];

      const pushCond = (op: ValueOP, value: unknown): void => {
        searchcond.searchconds!.push({
          condtype: 'DEFIELD',
          condop: op,
          fieldname: fieldKey,
          value,
        });
      };

      const dataRange = getDateRange(fieldValue, format);
      if (dataRange) {
        pushCond(ValueOP.GT_AND_EQ, dataRange.start);
        pushCond(ValueOP.LT_AND_EQ, dataRange.end);
      } else {
        pushCond(ValueOP.EQ, fieldValue);
      }
    };

    this.groupParams.length
      ? this.groupParams.forEach(g =>
          addParam(g.groupFieldId, g.dateFormat?.slice(-1)[0]),
        )
      : (addParam(catalogField, groupMode?.toLowerCase()),
        addParam(seriesField));

    return ibiz.commands.execute(
      OpenAppViewCommand.TAG,
      navAppViewId,
      this.context,
      { searchconds: [searchcond] },
      { ctx: this.view.getCtx(), noWaitRoute: true },
    );
  }

  /**
   * @description 初始化图表
   * @private
   * @memberof OdooChartController
   */
  initChart(chart: EChartsType): void {
    this.chart = chart;
    this.chart.on('click', params => {
      const data = (params.data as IData[])[2];
      const item = this.items.find(
        i =>
          i[this.dataEntity!.keyAppDEFieldId!] ===
          data[this.dataEntity!.keyAppDEFieldId!],
      );
      if (item) this.handleChartClick(item);
    });
    if (this.options) this.updateChart();
  }

  /**
   * @description 部件加载后处理
   * @param {MDCtrlLoadParams} args
   * @param {IData[]} items
   * @returns {*}  {Promise<IData[]>}
   * @memberof OdooChartController
   */
  async afterLoad(args: MDCtrlLoadParams, items: IData[]): Promise<IData[]> {
    const result = await super.afterLoad(args, items);
    // 保存原始数据
    this.items = clone(items);
    return result;
  }

  /**
   * @description 初始化测量集合
   * @private
   * @memberof OdooChartController
   */
  private initSurveys(): void {
    const { userParam } = this.model;
    if (userParam?.SURVEY) {
      try {
        const survey = JSON.parse(userParam.SURVEY);
        const keys = Object.keys(survey);
        if (keys.length) {
          keys.forEach(key => {
            this.state.surveys.push({
              label: survey[key],
              value: key,
            });
          });
        }
      } catch (error) {
        ibiz.log.error('测量参数解析错误：', error);
      }
    }
    this.state.surveys.push({
      label: '计数',
      value: 'srfcount',
    });
  }

  /**
   * @description 序列变更
   * @param {IDEChartSeries} series
   * @returns {*}  {void}
   * @memberof OdooChartController
   */
  async onSeriesChange(series: IDEChartSeries): Promise<void> {
    if (series.id === this.state.selectSeries.id) return;
    this.state.selectSeries = clone(series);
    await this.calcSeriesesOption();
  }

  /**
   * @description 测量变更
   * @param {string} value
   * @returns {*}  {void}
   * @memberof OdooChartController
   */
  async onSurveyChange(value: string): Promise<void> {
    const { selectSurvey } = this.state;
    if (value === selectSurvey) return;
    this.state.selectSurvey = value;
    await this.calcSeriesesOption();
  }

  /**
   * @description 获取文本属性标识
   * @private
   * @param {string} fieldId
   * @returns {*}  {string}
   * @memberof OdooChartController
   */
  private getTextFieldId(fieldId: string): string {
    const deRss = this.dataEntity.minorAppDERSs?.find(
      rss => rss.parentAppDEFieldId === fieldId,
    );
    return (deRss as IModel)?.parentTextAppDEFieldId || fieldId;
  }

  /**
   * @description 计算序列属性
   * @private
   * @returns {*}  {Promise<void>}
   * @memberof OdooChartController
   */
  private async calcSeriesField(): Promise<void> {
    const others = this.groupParams.slice(1);
    const { selectSeries, dechartSerieses, items } = this.state;
    if (!others.length) {
      const series = dechartSerieses.find(s => s.id === selectSeries.id);
      selectSeries.seriesField = series?.seriesField;
    } else {
      const app = ibiz.hub.getApp(this.context.srfappid);
      const seriesFields = await Promise.all(
        others.map(async other => {
          if (
            other.groupCodeListId &&
            !this.seriesFieldCodelist.has(other.groupCodeListId)
          ) {
            const codeListItems = await app.codeList.get(
              other.groupCodeListId,
              this.context,
            );
            this.seriesFieldCodelist.set(other.groupCodeListId, codeListItems);
          }
          return {
            codeListId: other.groupCodeListId,
            dateFormat: other.dateFormat?.slice(-1)[0],
            fieldId: this.getTextFieldId(other.groupFieldId),
          };
        }),
      );
      const seriesField = seriesFields.map(s => s.fieldId).join('_');
      // 格式化序列属性数据
      items.forEach(item => {
        seriesFields.forEach(field => {
          const key = field.fieldId.toLowerCase();
          let value = item[key];
          if (field.codeListId) {
            value = this.seriesFieldCodelist
              .get(field.codeListId)
              ?.find(l => l.value === value)?.text;
          } else if (field.dateFormat) {
            value = formatDate(item[key], field.dateFormat);
          }
          Object.assign(item, {
            [key]: value,
          });
        });
        // 如果序列属性超过一个，则需要手动构建新的序列属性
        if (seriesFields.length > 1)
          Object.assign(item, {
            [seriesField]: seriesFields
              .map(s => item[s.fieldId.toLowerCase()] || '无')
              .join(' / '),
          });
      });
      selectSeries.seriesField = seriesField;
    }
  }

  /**
   * @description 计算序列配置
   * @returns {*}  {Promise<void>}
   * @memberof OdooChartController
   */
  async calcSeriesesOption(): Promise<void> {
    const { dechartSerieses, selectSeries, stacked } = this.state;
    // 重置数据
    this.state.items = clone(this.items);
    const series = dechartSerieses.find(s => s.id === selectSeries.id);
    selectSeries.userParam = {};
    // 重置用户配置
    Object.assign(selectSeries.userParam, series?.userParam || {});
    // 根据分组参数计算序列配置
    if (this.groupParams.length) {
      // 第一项覆盖分类属性配置，其它项覆盖序列名称属性
      const catalog = this.groupParams[0];
      selectSeries.catalogField = this.getTextFieldId(catalog.groupFieldId);
      selectSeries.catalogCodeListId = catalog.groupCodeListId;
      const groupMode = catalog.groupCodeListId
        ? 'CODELIST'
        : catalog.dateFormat?.slice(-1)[0]?.toUpperCase();
      selectSeries.groupMode = groupMode === 'WEEK' ? 'YEARWEEK' : groupMode;
      await this.calcSeriesField();
    }
    // 设置序列值属性
    if (this.state.selectSurvey)
      selectSeries.valueField = this.state.selectSurvey;
    // 折线图默认光滑曲线
    if (selectSeries.echartsType === 'line')
      Object.assign(selectSeries.userParam, {
        'EC.smooth': true,
      });
    if (
      selectSeries.echartsType &&
      stacked &&
      ['bar', 'line'].includes(selectSeries.echartsType)
    ) {
      Object.assign(selectSeries.userParam, {
        'EC.stack': 'stack',
      });
      if (selectSeries.echartsType === 'line')
        Object.assign(selectSeries.userParam, {
          'EC.areaStyle': {},
        });
    }
    await this.initGenerator();
  }

  /**
   * @description 执行分组
   * @param {IApiMDGroupParams[]} arg
   * @param {IParams} [_params]
   * @returns {*}  {Promise<void>}
   * @memberof OdooChartController
   */
  async execGroup(arg: IApiMDGroupParams[], _params?: IParams): Promise<void> {
    this.groupParams = arg;
    const { selectSeries, dechartSerieses } = this.state;
    if (!this.groupParams.length) {
      const series = dechartSerieses.find(s => s.id === selectSeries.id);
      if (series) this.state.selectSeries = clone(series);
    }
    await this.calcSeriesesOption();
  }
}
